﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class Material
{
    /// <summary>
    /// 材质表面光线变化过程
    /// </summary>
    /// <param name="ray"></param>
    /// <param name="record"></param>
    /// <param name="attaenuation">衰减</param>
    /// <param name="scattered">散射射线</param>
    /// <returns></returns>
    public abstract bool scatter(Ray ray, HitRecord record, ref Color attaenuation, ref Ray scattered);
}

public class _M
{
    public static Vector3 GetRandomPointInUnitSphere()
    {
        Vector3 p = 2f * new Vector3(UnityEngine.Random.Range(0f, 1f), UnityEngine.Random.Range(0f, 1f), UnityEngine.Random.Range(0f, 1f)) - Vector3.one;
        p = p.normalized * UnityEngine.Random.Range(0f, 1f);

        return p;
    }
    public static Vector3 Reflect(Vector3 vin, Vector3 normal)
    {
        return vin - 2 * Vector3.Dot(vin, normal) * normal;
    }
    public static bool Refract(Vector3 v, Vector3 n, float ni_over_nt, ref Vector3 refracted)
    {
        Vector3 uv = v.normalized;
        float dt = Vector3.Dot(uv, n);
        float discriminant = 1f - ni_over_nt * ni_over_nt * (1 - dt * dt);
        if (discriminant > 0)
        {
            refracted = ni_over_nt * (v - n * dt) - n * Mathf.Sqrt(discriminant);
            return true;
        }
        else
        {
            return false;
        }
    }

    /// <summary>
    /// Schilick近似菲涅尔反射
    /// </summary>
    /// <returns>返回的是菲涅尔反射比</returns>
    public static float schlick(float cos, float ref_idx)
    {
        float r0 = (1 - ref_idx) / (1 + ref_idx);
        r0 *= r0;
        return r0 + (1 - r0) * Mathf.Pow((1 - cos), 5);
    }

    /// <summary>
    /// 这个取的是圆面而不是球
    /// </summary>
    /// <returns></returns>
    public static Vector3 GetRandomPointInUnitDisk()
    {
        Vector3 p = 2f * new Vector3(_M.R(), _M.R(), 0) - new Vector3(1, 1, 0);
        p = p.normalized * _M.R();
        return p;
    }
    public static float R()
    {
        return Random.Range(0f, 1f);
    }

    public static HitableList GetRandom_Scene()
    {
        HitableList list =new HitableList();
        list.list.Add(new Sphere(new Vector3(0f, -1000f, 0f), 1000f, new Lambertian(Color.gray)));
        for (int a = -4; a < 4; a++)
            for (int b = -4; b < 4; b++)
            {
                float choose_mat = R();
                Vector3 center = new Vector3(a + 0.9f * R(), 0.2f, b + 0.9f * R());
                if (Vector3.Distance(center, new Vector3(4, 0.2f, 4)) > 0.9f)
                {
                    if (choose_mat < 0.8f)
                        list.list.Add(new Sphere(center, 0.2f, new Lambertian(new Color(R() * R(), R() * R(), R() * R()))));
                    else if (choose_mat < 0.95f)
                        list.list.Add(new Sphere(center, 0.2f, new Metal(new Color(0.5f + 0.5f * R(), 0.5f + 0.5f * R(), 0.5f + 0.5f * R()), 0.5f * R())));
                    else
                        list.list.Add(new Sphere(center, 0.2f, new Dielectrics(1.5f)));
                }
            }
        list.list.Add(new Sphere(new Vector3(0.5f, 1, -0.5f), 1, new Dielectrics(1.5f)));
        list.list.Add(new Sphere(new Vector3(-2, 1, -0.5f), 1, new Lambertian(new Color(0.9f, 0.5f, 0.1f))));
        list.list.Add(new Sphere(new Vector3(2.5f, 1, -0.5f), 1, new Metal(new Color(0.7f, 0.6f, 0.5f), 0)));
        return list;
    }
}